package com.yds.socket.client;


import com.yds.socket.CodeType;
import com.yds.socket.bean.OtherMsgBean;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 作者:     YDS
 * 创建时间: 2023/2/16 9:09
 * 页面描述: 总结
 * 1、在客户端或者服务端通过socket.shutdownOutput()都是单向关闭的，即关闭客户端的输出流并不会关闭服务端的输出流，所以是一种单方向的关闭流；
 * 2、通过socket.shutdownOutput()关闭输出流，但socket仍然是连接状态，连接并未关闭
 * 3、如果直接关闭输入或者输出流，即：in.close()或者out.close()，会直接关闭socket
 */
public class SocketClient {
    private final int SOCKET_CONNECT_TIMEOUT = 20 * 1000;

    private Timer timer;
    private TimerTask timerTask;

    private ClientCallback callback;
    private ExecutorService threadPool;
    private Socket socket = null;
    private OutputStream outputStream;
    private ClientThread clientThread;

    /**
     * 连接Socket服务端
     *
     * @param ip       服务器地址
     * @param port     服务器端口
     * @param callback 客户端回调
     */
    public void connectSocketServer(String ip, int port,  ClientCallback callback) {
        this.callback = callback;
        // 允许重复连接同一个服务端，先断后连原则
        disconnectSocketServer();

        // 开启Timer，轮训判断客户端状态
        timer = new Timer();
        timerTask = new TimerTask() {
            @Override
            public void run() {
                // 测试客户端是否断开连接
                try {
                    if (null != socket) {
                        socket.sendUrgentData(1);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    callback.otherMsg(new OtherMsgBean(CodeType.DISCONNECT_SOCKET, socket));
                    disconnectSocketServer();
                }
            }
        };
        timer.schedule(timerTask, 1000, 2000);

        if (null == threadPool || threadPool.isShutdown()) {
            // 初始化缓存线程池
            threadPool = Executors.newCachedThreadPool();
        }

        threadPool.submit(() -> {
            socket = new Socket();
            try {
                socket.bind(null);
                InetSocketAddress inetSocketAddress = new InetSocketAddress(ip, port);
                socket.connect(inetSocketAddress, SOCKET_CONNECT_TIMEOUT);
                socket.sendUrgentData(1);

                clientThread = new ClientThread(socket, callback);
                clientThread.start();
            } catch (IOException e) {
                e.printStackTrace();
                callback.otherMsg(new OtherMsgBean(CodeType.EXCEPTION_MSG, "连接Socket服务端: " + e.getMessage()));
            }
        });
    }

    /**
     * 断开连接Socket服务端
     */
    public void disconnectSocketServer() {
        if (null != clientThread) {
            clientThread.interrupt();
            if (null != callback) {
                callback.otherMsg(new OtherMsgBean(CodeType.SUCCESS_MSG, "断开连接Socket服务端 clientThread.interrupt()"));
            }
        }

        if (null != threadPool && !threadPool.isShutdown()) {
            threadPool.shutdownNow();
            threadPool = null;
            if (null != callback) {
                callback.otherMsg(new OtherMsgBean(CodeType.SUCCESS_MSG, "断开连接Socket服务端 threadPool.shutdownNow()"));
            }
        }

        try {
            if (null != socket) {
                if (!socket.isInputShutdown()) {
                    socket.shutdownInput();
                }
                if (!socket.isOutputShutdown()) {
                    socket.shutdownOutput();
                }
                if (!socket.isClosed()) {
                    socket.close();
                }
                socket = null;
                if (null != callback) {
                    callback.otherMsg(new OtherMsgBean(CodeType.SUCCESS_MSG, "断开连接Socket服务端 socket.close()"));
                }

                if (null != outputStream) {
                    outputStream.close();
                    if (null != callback) {
                        callback.otherMsg(new OtherMsgBean(CodeType.SUCCESS_MSG, "断开连接Socket服务端 outputStream.close()"));
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            socket = null;
            if (null != callback) {
                callback.otherMsg(new OtherMsgBean(CodeType.EXCEPTION_MSG, "断开连接Socket服务端: " + e.getMessage()));
            }
        }
    }

    /**
     * 向服务端发送消息
     */
    public void sendMsgToServer(String msg) {
        if (null == threadPool || threadPool.isShutdown()) {
            if (null != callback) {
                callback.otherMsg(new OtherMsgBean(CodeType.ERROR_MSG, "向服务端发送消息失败，已断开连接: threadPool = null | threadPool.isShutdown()"));
            }
        } else {
            threadPool.submit(() -> {
                if (null != socket) {
                    if (!socket.isClosed()) {
                        try {
                            outputStream = socket.getOutputStream();
                            outputStream.write(msg.getBytes());
                            outputStream.flush();
                            if (null != callback) {
                                callback.otherMsg(new OtherMsgBean(CodeType.SUCCESS_MSG, "发送到服务端成功: " + msg));
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                            if (null != callback) {
                                callback.otherMsg(new OtherMsgBean(CodeType.EXCEPTION_MSG, "向服务端发送消息失败: " + e.getMessage()));
                            }
                        }
                    } else {
                        if (null != callback) {
                            callback.otherMsg(new OtherMsgBean(CodeType.ERROR_MSG, "向服务端发送消息失败: socket.isClosed()"));
                        }
                    }
                } else {
                    if (null != callback) {
                        callback.otherMsg(new OtherMsgBean(CodeType.ERROR_MSG, "向服务端发送消息失败: Socket Exception: socket = null"));
                    }
                }
            });
        }
    }

}
